package work.linruchang.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.*;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import lombok.SneakyThrows;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.Writer;
import java.util.Map;
import java.util.stream.Stream;

/**
 * 增强的Freemarker模板工具
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2022/11/19
 * @since 1.8
 **/
public class EnhanceFreemarkerUtil {

    private static BeansWrapper beansWrapper = new BeansWrapper(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
    private static TemplateHashModel staticModels = beansWrapper.getStaticModels();

    /**
     * 临时模板配置相关信息
     */
    private static String temporaryTemplateDir = "temp/EnhanceFreemarkerUtil";
    private static File temporaryTemplateDirFile = FileUtil.mkdir(temporaryTemplateDir);
    private static Configuration tempConfiguration = createConfigurationByFileDir(temporaryTemplateDir);

    @SneakyThrows
    public static TemplateModel getStaticModel(Class clazz) {
        TemplateModel templateModel = staticModels.get(ClassUtil.getClassName(clazz, false));
        return templateModel;
    }

    public static <T extends Map> T addStaticModels(T staticModelContainer, Class... clazzs) {
        if (!ObjectUtil.hasNull(staticModelContainer, clazzs)) {
            Stream.of(clazzs).forEach(clazz -> {
                staticModelContainer.put(ClassUtil.getClassName(clazz, true), getStaticModel(clazz));
            });
        }
        return staticModelContainer;
    }

    /**
     * 生成Freemarker配置实例
     *
     * @param tmeplateBasePackagePath 模板文件所在的相对目录
     * @return
     */
    public static Configuration createConfigurationByPackagePath(String tmeplateBasePackagePath) {
        Assert.notBlank(tmeplateBasePackagePath, "Freemarker相对目录缺失，请检查");
        Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        configuration.setClassLoaderForTemplateLoading(EnhanceFreemarkerUtil.class.getClassLoader(), tmeplateBasePackagePath);
        // 3.设置字符集
        configuration.setDefaultEncoding("utf-8");
        return configuration;
    }

    /**
     * 生成Freemarker配置实例
     *
     * @param fileDirPath 模板文件所在的文件目录  目录不存在则帮你创建
     * @return
     */
    @SneakyThrows
    public static Configuration createConfigurationByFileDir(String fileDirPath) {
        Assert.notBlank(fileDirPath, "Freemarker文件目录缺失，请检查");
        Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 1.设置模板所在的目录
        File tempDir = FileUtil.mkdir(fileDirPath);
        configuration.setDirectoryForTemplateLoading(tempDir);
        // 2.设置字符集
        configuration.setDefaultEncoding("utf-8");
        return configuration;
    }


    /**
     * 模板渲染
     *
     * @param tmeplateBasePackagePath 模板文件所在的相对目录
     * @param templateName            模板文件名
     * @param templateRenderParams    模板文件所需填充的参数
     * @return
     */
    @SneakyThrows
    public static String renderTemplate(String tmeplateBasePackagePath, String templateName, Dict templateRenderParams) {

        Assert.notBlank(templateName, "Freemarker模板名缺失，请检查");

        // 1. 配置
        Configuration configuration = createConfigurationByPackagePath(tmeplateBasePackagePath);

        // 2. 开始渲染
        return renderTemplate(configuration, templateName, templateRenderParams);

    }

    /**
     * 模板渲染
     *
     * @param configuration        配置
     * @param templateName         模板文件名
     * @param templateRenderParams 模板文件所需填充的参数
     * @return
     */
    @SneakyThrows
    public static String renderTemplate(Configuration configuration, String templateName, Dict templateRenderParams) {
        Assert.notNull(configuration, "Freemarker模板配置实例缺失，请检查");

        // 1. 获取模板实例
        Template template = configuration.getTemplate(templateName);

        // 2. 渲染渲染
        return renderTemplate(template, templateRenderParams);
    }

    /**
     * 模板渲染
     *
     * @param template             待渲染的模板
     * @param templateRenderParams 模板文件所需填充的参数
     * @return
     */
    @SneakyThrows
    public static String renderTemplate(Template template, Dict templateRenderParams) {

        // 1. 填充公共模板参数
        templateRenderParams = ObjUtil.defaultIfNull(templateRenderParams, Dict.create());
        templateRenderParams = EnhanceFreemarkerUtil.addStaticModels(templateRenderParams, DateUtil.class, DatePattern.class, CollUtil.class, StrUtil.class, IdUtil.class, ClassUtil.class);
        // 2. 模板渲染
        Writer resultWriter = new CharArrayWriter();
        template.process(templateRenderParams, resultWriter);
        return resultWriter.toString();

    }

    /**
     * 模板渲染
     *
     * @param templateContent      待渲染的模板内容
     * @param templateRenderParams 模板文件所需填充的参数
     * @return
     */
    @SneakyThrows
    public static String renderTemplate(String templateContent, Dict templateRenderParams) {
        File tempFile = null;
        try {
            Assert.notBlank(templateContent, "Freemarker模板内容缺失，请检查");

            tempFile = FileUtil.createTempFile(temporaryTemplateDirFile);
            FileUtil.writeUtf8String(templateContent, tempFile);

            // 4.加载模板                                    模板名自动在模板所在目录寻找
            Template template = tempConfiguration.getTemplate(tempFile.getName());

            return renderTemplate(template, templateRenderParams);
        }finally {
            FileUtil.del(tempFile);
        }
    }

}
